/*
 * Copyright (c) 2021 Huawei Device Co., Ltd.
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *   http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

import Log from './log.js';

const {mergeModifiers, ProfileHelpers} = require('./profile_helpers');

export class MixpanelPeople extends ProfileHelpers() {
  constructor(mpInstance) {
    super();
    this.mixpanel = mpInstance;
    this.endpoint = '/engage';
  }

  /**
   * people.setOnce(distinctId, prop, to, modifiers, callback)
   * ---
   * The same as people.set but in the words of mixpanel:
   * mixpanel.people.setOnce
   *
   * " This method allows you to set a user attribute, only if
   *     it is not currently set. It can be called multiple times
   *     safely, so is perfect for storing things like the first date
   *     you saw a user, or the referrer that brought them to your
   *     website for the first time. "
   */
  setOnce(distinctId, prop, to, modifiers, callback) {
    const identifiers = { $distinctId: distinctId };
    this._set(prop, to, modifiers, callback, { identifiers, setOnce: true });
  }

  /**
   * people.set(distinctId, prop, to, modifiers, callback)
   * ---
   * set properties on an user record in engage
   * usage:
   *     mixpanel.people.set('bob', 'gender', 'm');
   *     mixpanel.people.set('joe', {
   *         'company': 'acme',
   *         'plan': 'premium'
   *     });
   */
  set(distinctId, prop, to, modifiers, callback) {
    const identifiers = { $distinctId: distinctId };
    this._set(prop, to, modifiers, callback, { identifiers });
  }

  /**
   * people.increment(distinctId, prop, by, modifiers, callback)
   * ---
   * increment/decrement properties on an user record in engage
   *
   * usage:
   *
   *  mixpanel.people.increment('bob', 'page_views', 1);
   *
   *  // or, for convenience, if you're just incrementing a counter by 1, you can
   *  // simply do
   *  mixpanel.people.increment('bob', 'page_views');
   *
   *  // to decrement a counter, pass a negative number
   *  mixpanel.people.increment('bob', 'credits_left', -1);
   *
   *  // like mixpanel.people.set(), you can increment multiple properties at once:
   *  mixpanel.people.increment('bob', {
   *      counter1: 1,
   *      counter2: 3,
   *      counter3: -2
   *  });
   */
  increment(distinctId, prop, by, modifiers, callback) {
    var $add = {};

    if (typeof (prop) === 'object') {
      if (typeof (by) === 'object') {
        callback = modifiers;
        modifiers = by;
      } else {
        callback = by;
      }

      for (const key in prop) {
        if (Object.prototype.hasOwnProperty.call(prop, key)) {
          const val = prop[key];
          const valf = parseFloat(val);
          if (isNaN(valf) && this.mixpanel.config.debug) {
            Log.showError('Invalid increment value passed to mixpanel.people.increment - must be a number');
            Log.showError('Passed ' + key + ':' + val);
          } else if (!isNaN(valf)) {
            $add[key] = val;
          }
        }
      }
    } else {
      if (typeof (by) === 'number' || !by) {
        by = by || 1;
        $add[prop] = by;
        if (typeof (modifiers) === 'function') {
          callback = modifiers;
        }
      } else if (typeof (by) === 'function') {
        callback = by;
        $add[prop] = 1;
      } else {
        callback = modifiers;
        modifiers = (typeof (by) === 'object') ? by : {};
        $add[prop] = 1;
      }
    }

    var data = {
      '$add': $add,
      '$token': this.mixpanel.token,
      '$distinctId': distinctId
    };

    data = mergeModifiers(data, modifiers);

    if (this.mixpanel.config.debug) {
      Log.showInfo('Sending the following data to Mixpanel (Engage):');
      Log.showInfo(data);
    }

    this.mixpanel.sendRequest({ method: 'GET', endpoint: '/engage', data: data }, callback);
  }

  /**
   * people.append(distinctId, prop, value, modifiers, callback)
   * Append a value to a list-valued people analytics property.
   * usage:
   *    // append a value to a list, creating it if needed
   *    mixpanel.people.append('bob', 'pages_visited', 'homepage');
   *    // like mixpanel.people.set(), you can append multiple properties at once:
   *    mixpanel.people.append('bob', {
   *        list1: 'bob',
   *        list2: 123
   *    });
   */
  append(distinctId, prop, value, modifiers, callback) {
    var $append = {};

    if (typeof (prop) === 'object') {
      if (typeof (value) === 'object') {
        callback = modifiers;
        modifiers = value;
      } else {
        callback = value;
      }
      Object.keys(prop).forEach(function (key) {
        $append[key] = prop[key];
      });
    } else {
      $append[prop] = value;
      if (typeof (modifiers) === 'function') {
        callback = modifiers;
      }
    }

    var data = {
      '$append': $append,
      '$token': this.mixpanel.token,
      '$distinctId': distinctId
    };

    data = mergeModifiers(data, modifiers);

    if (this.mixpanel.config.debug) {
      Log.showInfo('Sending the following data to Mixpanel (Engage):');
      Log.showInfo(data);
    }

    this.mixpanel.sendRequest({ method: 'GET', endpoint: '/engage', data: data }, callback);
  }

  /**
   * people.trackCharge(distinctId, amount, properties, modifiers, callback)
   *    ---
   *    Record that you have charged the current user a certain
   *    amount of money.
   *    usage:
   *        // charge a user $29.99
   *        mixpanel.people.trackCharge('bob', 29.99);
   *        // charge a user $19 on the 1st of february
   *        mixpanel.people.trackCharge('bob', 19, { '$time': new Date('feb 1 2012') });
   */
  trackCharge(distinctId, amount, properties, modifiers, callback) {
    if (typeof (properties) === 'function' || !properties) {
      callback = properties || function () {
      };
      properties = {};
    } else {
      if (typeof (modifiers) === 'function' || !modifiers) {
        callback = modifiers || function () {
        };
        if (properties.$ignoreTime || Object.prototype.hasOwnProperty.call(properties, '$ip')) {
          modifiers = {};
          Object.keys(properties).forEach(function (key) {
            modifiers[key] = properties[key];
            delete properties[key];
          });
        }
      }
    }

    if (typeof (amount) !== 'number') {
      amount = parseFloat(amount);
      if (isNaN(amount)) {
        Log.showError('Invalid value passed to mixpanel.people.trackCharge - must be a number');
        return;
      }
    }

    properties.$amount = amount;

    if (Object.prototype.hasOwnProperty.call(properties, '$time')) {
      var time = properties.$time;
      if (Object.prototype.toString.call(time) === '[object Date]') {
        properties.$time = time.toISOString();
      }
    }

    var data = {
      '$append': { '$transactions': properties },
      '$token': this.mixpanel.token,
      '$distinctId': distinctId
    };

    data = mergeModifiers(data, modifiers);

    if (this.mixpanel.config.debug) {
      Log.showInfo('Sending the following data to Mixpanel (Engage):');
      Log.showInfo(data);
    }

    this.mixpanel.sendRequest({ method: 'GET', endpoint: '/engage', data: data }, callback);
  }

  /**
   * people.clearCharges(distinctId, modifiers, callback)
   * ---
   * Clear all the current user's transactions.
   * usage:
   *      mixpanel.people.clearCharges('bob');
   */
  clearCharges(distinctId, modifiers, callback) {
    var data = {
      '$set': { '$transactions': [] },
      '$token': this.mixpanel.token,
      '$distinctId': distinctId
    };

    if (typeof (modifiers) === 'function') {
      callback = modifiers;
    }

    data = mergeModifiers(data, modifiers);

    if (this.mixpanel.config.debug) {
      Log.showInfo("Clearing this user's charges:", distinctId);
    }
    this.mixpanel.sendRequest({ method: 'GET', endpoint: '/engage', data: data }, callback);
  }

  /**
   * people.deleteUser(distinctId, modifiers, callback)
   * ---
   * delete an user record in engage
   * usage:
   *     mixpanel.people.deleteUser('bob');
   */
  deleteUser(distinctId, modifiers, callback) {
    const identifiers = { $distinctId: distinctId };
    this._deleteProfile({ identifiers, modifiers, callback });
  }

  /**
   * people.remove(distinctId, data, modifiers, callback)
   * ---
   * remove a value from a list-valued user profile property.
   * usage:
   *     mixpanel.people.remove('bob', {'browsers': 'firefox'});
   *     mixpanel.people.remove('bob', {'browsers': 'chrome', 'os': 'linux'});
   */
  remove(distinctId, data, modifiers, callback) {
    const identifiers = { '$distinctId': distinctId };
    this._remove({ identifiers, data, modifiers, callback })
  }

  /**
   * people.union(distinctId, data, modifiers, callback)
   * ---
   * merge value(s) into a list-valued people analytics property.
   * usage:
   *     mixpanel.people.union('bob', {'browsers': 'firefox'});
   *     mixpanel.people.union('bob', {'browsers': ['chrome'], os: ['linux']});
   */
  union(distinctId, data, modifiers, callback) {
    const identifiers = { $distinctId: distinctId };
    this._union({ identifiers, data, modifiers, callback });
  }

  /**
   * people.unset(distinctId, prop, modifiers, callback)
   * ---
   * delete a property on an user record in engage
   * usage:
   *     mixpanel.people.unset('bob', 'page_views');
   *     mixpanel.people.unset('bob', ['page_views', 'last_login']);
   */
  unset(distinctId, prop, modifiers, callback) {
    const identifiers = { $distinctId: distinctId };
    this._unset({ identifiers, prop, modifiers, callback });
  }
}
